using System;
using System.ComponentModel;
using System.ComponentModel.Design;

using System.Drawing;
using System.Drawing.Drawing2D;

namespace Camalon.Drawing
{
	[ToolboxItem( true )]
	[ToolboxBitmap( typeof( ShapePen ), "Components.LinearGradientFill.bmp")]
	public class LinearGradientFill:MulticolorFill
	{
		private bool				_gammaCorrection;
		private int					_rotation  = 0;
		private bool				_transform = true;


		public LinearGradientFill():this(false){}
		public LinearGradientFill(bool gammaCorrection):base(FillType.LinearGradient)
		{
			_gammaCorrection	= gammaCorrection;
		}



		public Brush GetBrush( RectangleF bounds, float rotation )
		{
			if( !_transform ) rotation =  _rotation;

			LinearGradientBrush brush;
			bounds.Inflate( 1f,1f);

			bounds = TileBounds( bounds );


			if( base.Blend == null) brush = new LinearGradientBrush( bounds, Color.Black, Color.Transparent, rotation);
			else
			{
				brush = new LinearGradientBrush( bounds, Color.Empty, Color.Empty, rotation);
				//brush.InterpolationColors = base.Blend;

				if( _intensity.Length < 1)
				{
					brush.InterpolationColors = base.Blend;
				}
				else
				{
					Blend blend = new Blend( _intensity.Length );
					float[] factors		= new float[_intensity.Length];
					float[] positions	= new float[_intensity.Length];
					for (int i = 0; i < _intensity.Length;i++) 
					{
						ColorIntensity intensity = (ColorIntensity)_intensity[i];
						factors[i] = intensity.Intensity;
						positions[i] = intensity.Position;
					}
					
					blend.Factors = factors;
					blend.Positions = positions; 
					brush.LinearColors = base.Blend.Colors;
					brush.Blend = blend;
				}
			}

			if( base.Tile.IsEmpty) brush.WrapMode = WrapMode.TileFlipXY;
			else brush.WrapMode = base.WrapMode == WrapMode.Clamp ? WrapMode.Tile: base.WrapMode;
			
			brush.GammaCorrection	= _gammaCorrection;

			return brush;
		}


		public Brush GetBrush( RectangleF bounds )
		{
			return GetBrush( bounds, null);
		}


		public Brush GetBrush(RectangleF bounds, Matrix matrix)
		{

			if( _transform )
			{
				LinearGradientBrush brush = (LinearGradientBrush)GetBrush( bounds, 0);
				if( matrix != null && !matrix.IsIdentity )  brush.Transform = matrix;
				return brush;
			}
			else
			{
				if( matrix == null || matrix.IsIdentity ) return (LinearGradientBrush)GetBrush( bounds, _rotation);

				PointF[] pts = RectangleUtil.GetPoints( bounds);
				matrix.TransformPoints( pts);
				bounds = RectangleUtil.GetBounds( pts );
				LinearGradientBrush brush = (LinearGradientBrush)GetBrush( bounds, _rotation);
				return brush;
			}
		}



		public Brush GetPenBrush(RectangleF bounds, Matrix matrix, float penWidth )
		{
			bounds.Inflate( penWidth/2f,penWidth/2f); 
			return GetBrush( bounds, matrix);
		}

		public Brush GetPenBrush( RectangleF bounds, Matrix penTransform, ShapePen shapePen, int rotation )
		{
			if( shapePen == null )		throw new ArgumentException("Invalid argument. Can not be null.", "shapePen") ;
			if( shapePen.Fill == null ) throw new ArgumentException("Invalid argument. Can not be null.", "shapePen.Fill") ;
			if( shapePen.Fill.FillType != FillType.LinearGradient ) throw new ArgumentException("Invalid Fill type. Expected linear gradient fill.", "shapePen.Fill.FillType") ;
			

			LinearGradientFill fill = (LinearGradientFill)shapePen.Fill;
			RectangleF rect = new RectangleF(0,0,(shapePen.Width/2f),(shapePen.Width/2f));
					
			if(penTransform != null && !penTransform.IsIdentity )//new
			{
				PointF[] pts = RectangleUtil.GetPoints( rect );
				penTransform.TransformPoints( pts );
				rect = RectangleUtil.GetBounds( pts );
			}

			bounds.Inflate( rect.Size );

			return fill.GetBrush( bounds , rotation );
		}

		public Brush GetPenBrush( GraphicsPath path , Matrix penTransform, ShapePen shapePen , int rotation )
		{
			RectangleF b	= path.GetBounds();
			PointF center = RectangleUtil.CenterPoint(b);
			
			using(Pen pen = Outline.GetOutlinePen(shapePen))
			{
				if(penTransform != null && !penTransform.IsIdentity ) pen.Transform = penTransform;

				if( !_transform )
					using(GraphicsPath clone = path.Clone() as GraphicsPath)
					{
						clone.Widen( pen );
						b = clone.GetBounds();
						return GetBrush(b);
					}


				using(GraphicsPath clone = path.Clone() as GraphicsPath)
				{

					if( pen.Width > 1) clone.Widen( pen );

					if( rotation != 0)
					using(Matrix m = new Matrix())
					{
						m.RotateAt(-rotation , center,MatrixOrder.Append);
						clone.Transform( m );
					}

					b = clone.GetBounds();
					b.Inflate(1f,1f);
				}


				LinearGradientBrush brush = (LinearGradientBrush)GetBrush(b);

				if( rotation != 0)
				using(Matrix m2 = new Matrix())
				{
					m2.RotateAt( rotation, center,MatrixOrder.Append );
					brush.Transform = m2 ;
				}

				return brush;

			}
		}


		private ColorIntensity[] _intensity = new ColorIntensity[0];

		[DesignOnly( true)]
		protected virtual bool ShouldSerializeIntensity()	
		{
			return _intensity.Length > 0;
		}

		[RefreshProperties(RefreshProperties.All)]
		[Description("An array of intensity values that represents the color intensities to use at corresponding positions along the gradient.")]
		[DesignerSerializationVisibility( DesignerSerializationVisibility.Content)]
		public virtual ColorIntensity[] Intensity
		{	//Code copied from base.InterpolationColors
			get
			{
				if( _intensity == null) _intensity = new ColorIntensity[0];
				return _intensity.Clone() as ColorIntensity[];
			}
			set
			{ 

				if( value == null ) value = new ColorIntensity[0];
				
				if( value.Length > 1)
				{
					value[0].Position = 0f;
					value[value.Length-1].Position = 1f;
				}
				

				_intensity  = value;

				InvokeInternalChanged();
			}
		}

		

		[Description("A boolean value indicating whether gamma correction is enabled for this LinearGradientFill object.")]
		[DefaultValue(false)]
		[RefreshProperties(RefreshProperties.All)]
		public bool GammaCorrection
		{
			get{ return _gammaCorrection;}
			set
			{
				if( _gammaCorrection == value) return;

				_gammaCorrection = value;

				InvokeInternalChanged();
			}
		}

		[Description("A boolean value indicating whether gamma correction is enabled for this LinearGradientFill object.")]
		[DefaultValue(0)]
		[RefreshProperties(RefreshProperties.All)]
		public int Rotation
		{
			get{ return _rotation;}
			set
			{
				if( _rotation == value) return;

				_rotation = value;

				InvokeInternalChanged();
			}
		}


		[Description("A boolean value indicating whether gamma correction is enabled for this LinearGradientFill object.")]
		[DefaultValue(true)]
		[RefreshProperties(RefreshProperties.All)]
		public bool Transform
		{
			get{ return _transform;}
			set
			{
				if( _transform == value) return;

				_transform = value;

				InvokeInternalChanged();
			}
		}

	}
}
